package cheme.ProcessEditor.Parts.Streams.Tag;

import java.util.List;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.geometry.Transposer;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;

import cheme.ProcessEditor.Policies.ToolbarLayoutEditPolicy;
import cheme.ProcessModel.IChild;
import cheme.ProcessModel.IParent;
import cheme.ProcessModel.Cmds.AddCmd;
import cheme.ProcessModel.Cmds.DeleteCmd;
import cheme.ProcessModel.Cmds.MoveCmd;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.ITagComponent;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.TagComponent;

/*
 * Like most layout policies, this is where moveing visual elements around a 
 * container is handled, specifically, stream descriptors into/out of/up or down
 * a tag componnent list.
 */
public class PolicyTagComponentListLayout extends ToolbarLayoutEditPolicy
{
	@Override
	protected void showLayoutTargetFeedback(Request request)
	{
		/*
		 * Override to dissallow any show of feedback--drops aren't allowed here.
		 */
		
		boolean allowShow = false;
		
		if(REQ_CREATE.equals(request.getType()))
		{
			if((((CreateRequest)request).getNewObject() instanceof TagComponent))
			{
				allowShow = true;
			}
		}
		else if(REQ_ADD.equals(request.getType()) || REQ_MOVE.equals(request.getType()))
		{
			List<GraphicalEditPart> parts = ((ChangeBoundsRequest)request).getEditParts();
			
			if(parts.size() > 0)
			{
				/*
				 * All model elements have to be of TagComponent type
				 */
				
				allowShow = true;
				
				for(GraphicalEditPart part : parts)
				{
					allowShow &= (part.getModel() instanceof TagComponent);
				}			
			}
		}
		
		if(allowShow)
		{
			super.showLayoutTargetFeedback(request);
		}
	}
	
	/**
	 * This method is overridden from the superclass to calculate the <i>index</i> at which
	 * the children should be added. The index is determined by finding a reference EditPart,
	 * and adding the new child[ren] <em>after</em> that reference part. <code>null</code> is
	 * used to indicate that the child[ren] should be added at the beginning.
	 * <P>
	 * Subclasses must override {@link #createAddCommand(EditPart, EditPart)}, and should not
	 * override this method.
	 * @see org.eclipse.gef.editpolicies.LayoutEditPolicy#getAddCommand(Request)
	 */
	protected Command getAddCommand(Request req)
	{		
		ChangeBoundsRequest request = (ChangeBoundsRequest)req;
		List editParts = request.getEditParts();
		CompoundCommand command = new CompoundCommand();
	
		for (int i = 0; i < editParts.size(); i++)
		{
			EditPart child = (EditPart) editParts.get(i);
			
			command.add(createAddCommand(child, getInsertionRef(request)));
		}
		
		return command.unwrap();
	}
	
	/*
	 * Gives the editpart before which a new editpart will be inserted.
	 * This returns null if the editpart needs to be added to the end.
	 */
	protected EditPart getInsertionRef(Request request)
	{
		int index = getFeedbackIndexFor(request);
		List children = getHost().getChildren();
		EditPart editpart = null;
		
		if(-1 != index)
		{
			editpart = (EditPart)children.get(index);
		}
		
		return editpart;
	}

	/**
	 * @param request the Request
	 * @return the index for the insertion reference
	 */
	/*
	 * Returns the index of the index 
	 */
	protected int getFeedbackIndexFor(Request request)
	{
		List children = getHost().getChildren();
		
		if (children.isEmpty())
		{
			return -1;
		}
			
		Transposer transposer = new Transposer();
		transposer.setEnabled (!isHorizontal());
		
		Point p = transposer.t(getLocationFromRequest(request));

		// Current row bottom, initialize to above the top.
		int myCandidate = -1; //-1 indicate adding to the end of the children list

		EditPart myChild = null;
		Rectangle myRect = null;
		Rectangle rightHalf = null;
		Rectangle leftHalf = null;
				
		/*
		 * Basic plan, if it is in the top half of an editpart, return it's index to affect insertion prior to it,
		 * if it is in the bottom half, return one past its index to affect insertion after it.
		 */
		for (int i = 0; i < children.size(); i++)
		{
			myChild = (EditPart) children.get(i);
			myRect = transposer.t(getAbsoluteBounds(((GraphicalEditPart)myChild)));
			
			rightHalf = myRect.getCopy().setSize(myRect.width / 2, myRect.height);
			leftHalf = rightHalf.getCopy().setLocation(rightHalf.x + rightHalf.width, rightHalf.y);
			
			if(rightHalf.x <= p.x && p.x <= rightHalf.right())
			{
				myCandidate = i;
				break;
			}
			else if(leftHalf.x <= p.x && p.x <= leftHalf.right())
			{
				myCandidate = i + 1;
				break;
			}
		}
		
		if(children.size() <= myCandidate)
		{
			myCandidate = -1;
		}
				
		return myCandidate;
	}
	
	@Override
	protected Command createAddCommand(EditPart child, EditPart after)
	{		
		/*
		 * need to take into account that the afterChild could be null and will need to add the child to the end.
		 */
		Command cmd = null;
		

		IChild moveChild = (IChild)child.getModel();
		
		if(moveChild instanceof ITagComponent)
		{
			CompoundCommand compund = new CompoundCommand();
			IParent originalParent = moveChild.getParent();
			IParent newParent = (IParent)getHost().getModel();
			
			if(null == after)
			{
				compund.add(new MoveCmd(moveChild, originalParent, newParent, -1));
			}
			else
			{
				IChild afterChild = (IChild)after.getModel();
				compund.add(new MoveCmd(moveChild, afterChild, originalParent, newParent));
			}
			
			/*
			 * Are we moving the last element in the originalParent? If so, delete the
			 * list too.
			 */
			if(1 == originalParent.getChildren().size())
			{
				compund.add(new DeleteCmd((IChild)originalParent));
			}
			
			cmd = compund.unwrap();
		}
		
		return cmd;
	}

	@Override
	protected Command createMoveChildCommand(EditPart child, EditPart after)
	{		
		int newIndex = -1;
		
		if (after != null)
		{
			newIndex = getHost().getChildren().indexOf(after);
		}
		
		IChild childModel = (IChild) child.getModel();
		IParent parent = (IParent) getHost().getModel();

		MoveCmd command = new MoveCmd(childModel, parent, newIndex);
		
		return command;
		
//		return null;
	}

	/**
	 * @param request
	 * @return
	 */
	@Override
	protected Command getCreateCommand(CreateRequest request)
	{
		Command cmd = null;
		
		if(request.getNewObject() instanceof IChild)
		{
			IChild newChild = (IChild)request.getNewObject();
			EditPart after = getInsertionReference(request);
			int insertIndex = getHost().getChildren().indexOf(after);
			
			cmd = new AddCmd(newChild, (IParent)getHost().getModel(), insertIndex);
		}
		
		return cmd;
		
//		return null;
	}

	/**
	 * @param request
	 * @return
	 */
	protected Command getDeleteDependantCommand(Request request)
	{
		return null;
	}

}
